#!/usr/bin/env python
import sys
import ConfigParser
import MySQLdb
import cPickle
sys.path.append('..')
sys.path.append('../../sincronizacion/')
sys.path.append('../Database')
import payFile
#from ClinicHistory import *
#from Person import *
#from Soap import *
#from Sincronizacion import *
import ConnectionDB as conn
from proccesingQuery import DesingQuery 
from M2Crypto import SSL as SSL_LIB

HOST = '127.0.0.1'
PORT = 5050
BACKLOG = 5
BUFF_SIZE = 1024
SERVER = 1
CLIENT = 0
SSL_PATH = 'certificados/'
SSL_VERSION = 'sslv3'
SSL_CERT_SER = 'certificado-servidor.pem'
SSL_KEY_SER = 'claveprivada.pem'
SSL_CERT_AUTH = 'CAXplotacert.pem'

BD_CONFIG = '../settings_server.ini'
XML_SOURCE = '../XML/'
FILE_FOUND = '1'
FILE_NOFOUND = '0'

EXIST_ROW = '1'
NOEXIST_ROW = '0'

class SSL:
	""" Objeto que me proporciona
		la conexion segura con SSL """

	def __init__(self):
		self.ssl_path = SSL_PATH
		self.ssl_version = SSL_VERSION
		self.ssl_cert_ser = SSL_CERT_SER
		self.ssl_key_ser = SSL_KEY_SER
		self.ssl_cert_auth = SSL_CERT_AUTH

	def context(self):
		con = SSL_LIB.Context(self.ssl_version)
		con.load_cert(certfile = self.ssl_path + self.ssl_cert_ser,
				keyfile = self.ssl_path + self.ssl_key_ser,
				callback = self.pp_callback)
		con.load_verify_locations(self.ssl_path + self.ssl_cert_auth)
		con.set_verify(SSL_LIB.verify_peer | 
				SSL_LIB.verify_fail_if_no_peer_cert, depth = 9)
		return con

	def pp_callback(self, *args):
	# The passphrase is '1234'.
		return '18040808'

class Server:
	""" Objeto que me proporciona
		la conexion con el Cliente """

	def __init__(self, con):
		self.host = HOST
		self.port = PORT
		self.backlog = BACKLOG
		self.buff_size = BUFF_SIZE
		self.con = con

	def conect(self):
		bindsocket = SSL_LIB.Connection(self.con)
		bindsocket.bind((self.host,self.port))
		bindsocket.listen(self.backlog)
		print "Esperando por conexiones"
		(connectsocket, fromaddress) = bindsocket.accept()
		if connectsocket.get_peer_cert() is not None:
			print "Servidor: Certificado del Cliente Correcto"
			self.connectsocket = connectsocket
			self.bindsocket = bindsocket
		else:
			print "Server: No se pudo validar el Certificado" 
			sys.exit()

	def send(self, data):
		len_data = cPickle.dumps(len(data))
		lenn = len(data)
		self.connectsocket.send(len_data)
		i = 0 
		while lenn > 0:
			self.connectsocket.send(cPickle.dumps(data[i]))                          
			i += 1
			lenn -= 1

	def send_ex(self, data):
		len_data = cPickle.dumps(len(data))
		lenn = len(data)
		self.connectsocket.send(len_data)                                        
		i = 0
		while lenn > 0:
			tam = 80 
			self.connectsocket.send(cPickle.dumps(tam))
			j = 0
			info = gen_person(data[i])
			while tam > 0:
				self.connectsocket.send(cPickle.dumps(info[j]))
				tam -= 1
				j += 1
			i += 1
			lenn -= 1

	def recv(self):
		data = self.connectsocket.read()
		len_info = 0
		if len(data) != 0:
			len_info = cPickle.loads(data)
		list = []
		while len_info > 0:
			data = self.connectsocket.read()
			list.append(cPickle.loads(data))
			len_info -= 1
		return list

	def recv_message(self):
		data = self.connectsocket.read()
		return data

	def send_message(self,data):
		self.connectsocket.send(data)

	def close(self):
		self.connectsocket.close()
		self.bindsocket.close()

def sending(ser, list):
	if len(list) > 0:
		ser.send_message("1")
	else:
		ser.send_message("0")
def reciving(ser):
	return ser.recv_message()

def find(fle):
	return payFile.findFile(fle,XML_SOURCE)

def clasified_row(rowServer, rowClient, fieldKey):
	lista = [rowClient,[ ], rowServer]
	lenFieldKey = len(fieldKey)
	if len(rowServer) == 0 or len(rowClient) == 0:
		return lista
	numberField = len(rowServer[0])
	rowToRemoveServer = []
	rowToRemoveClient = []
	rtrs = 0
	rtrc = 0
	for rowC in rowClient:
		conflict = True
		for rowS in rowServer:
			for i in xrange(lenFieldKey):
				print fieldKey[i]
				if rowC[fieldKey[i]] != rowS[fieldKey[i]]:
					conflict = False
			if conflict == True:
				if rowS[numberField-1] != rowC[numberField-1]:
					rowToRemoveServer.append(rtrs)
					rowToRemoveClient.append(rtrc)
					break
			conflict = True	
			rtrs += 1
		rtrc += 1

	rowConflict = []

	for i in rowToRemoveClient:
		rowConflict.append(rowClient[i])
		del rowClient[i]
		
	for i in rowToRemoveServer:
		del rowServer[i]
		

	setReturn= [ rowClient, rowConflict , rowServer ]
	return setReturn



if __name__ == "__main__":
	ssl = SSL()
	context = ssl.context()
	while 1:
		server = Server(context)
		server.conect()
# inicializacion
		try:
			fileToSynchronized = reciving(server)             #archivo de configuracion de sincronizacion
			root = find(fileToSynchronized)
			if root == None:				 #si lo encontramos empezamos sincronizacion
				server.send(FILE_NOFOUND)
				print "Se solicito archivo %s y no se encontro" % fileToSynchronized
				continue
			else:
				server.send(FILE_FOUND)		
			print "Iniciando Sincronizacion"

# Recibimos lista de filas con cambios 
			regs_client = server.recv()
			
# Hacemos conexion a la base de datos			
			connection  = conn.connectionDB(root)
			connect = connection.connect()

# Buscamos registros del servidor con cambios 

			design = DesingQuery(root,SERVER)
			query =  design.querySelectForTable().next()
			keyField = design.getKeyField().next()
			regs_server = connection.executeSelectQuery(query, connect)
				
			setRows = clasified_row(regs_server, regs_client, keyField)
			fields = design.getFields().next()
			numberColumns = design.numberColumns().next()           #campo que indica operacion realizada

# Actualizando registros
			operation = ''
			for row in setRows[0]:
				if  row[numberColumns] == 'I':
					operation =  design.insertQuery(row,fields)
				else:
					operation = design.updateQuery(row,fields)
				connection.executeQuery(operation,connect)
				connection.executeQuery(design.querySynchronized(row,fields),connect)
					
# Enviando registros con conflicto
			sending(server,setRows[1])
			if len(setRows[1]) > 0:
				print "Existen registros con conflicto...."
				server.send(setRows[1])
				for row in setRows[1]:
					connection.executeQuery(design.queryConflict(row,fields),connect)
				
				 
# Enviando registros que han cambiado en el servidor	
			sending(server,setRows[2])
			if len(setRows[2]) > 0:
				print "Enviando registros al cliente"
				server.send(setRows[2])
				for row in setRows[2]:
					connection.executeQuery(design.querySynchronized(row,fields),connect)

		except KeyboardInterrupt:
			print "Keyboard Interrupt recieved"
			server.close()
